<?php

namespace Sharks\Core\Http\Routing;

use Sharks\Core\Exception\RouteResolveException;
use Sharks\Core\Support\Bag\ParameterBag;
use Sharks\Core\Http\Request;

/**
 * Route集合
 */
class RouteCollection {

    private $collection;
    private $reverse;

    public function __construct() {
        $this->collection = new ParameterBag;
        $this->reverse = new ParameterBag;
    }

    /**
     * 解析与注册映射类
     * @return [type] [description]
     */
    public function registerMap($verb, $newRoute, $oldRoute, array $mapParameter = array(), $desc = null) {
        $verb = strtoupper($verb);
        list($routeRexPattern, $parameters) = $this->resolveRoute($newRoute);

        $routeMap = array(
            'route' => $newRoute,
            'verb' => $verb,
            'desc' => $desc,
            'map' => array(
                'route' => $oldRoute,
                'params' => $mapParameter
            ),
            
        );

        if (isset($routeRexPattern) && !empty($routeRexPattern)) {// 是否有rex pattern
            $routeMap['routeRexPattern'] = $routeRexPattern;
        }

        if (isset($parameters) && !empty($parameters)) {// 是否有参数
            $routeMap['parameters'] = $parameters;
        }

        self::setRoute($verb . $newRoute, $routeMap);
    }

    /**
     *
     * 解析与注册Route
     * @param string $verb http方法
     * @param string $route 注册与解析访问路径
     * @param string $action 对应的执行类与方法 形式:moduleName.controllerName@functionName
     * @param array $middlewares 中间件, 使用别名数组
     * @param string $desc route说明
     * @throws RouteResolveException
     */
    public function register($verb, $route, $action, array $middlewares = array(), $desc = null) {
        $verb = strtoupper($verb);
        list($routeRexPattern, $parameters) = $this->resolveRoute($route);
        list($moduleName, $controllerName, $methodName) = $this->resolveAction($action);

        $routeMap = array(
            'route'=>$route,
            'verb'=>$verb,
            'desc'=>$desc,

            'action'=> array(
                'module'=>$moduleName,
                'controller'=>$controllerName,
                'method'=>$methodName,
            ),
            'middlewares'=>$middlewares
        );

        if (isset($routeRexPattern) && !empty($routeRexPattern)) {//是否有rex pattern
            $routeMap['routeRexPattern'] = $routeRexPattern;
        }
        if (isset($parameters) && !empty($parameters)) {// 是否有参数
            $routeMap['parameters'] = $parameters;
        }

        $this->setRoute($verb . $route, $routeMap);
    }

    /**
     * 通过正则表达式分析$route
     * 1. 通过正则表达式先分析有没有参数
     * 2. 拆解参数
     * 3. 并形成可分析的正则表达式
     * @param  [type] $route [description]
     * @return array [type]        [description]
     */
    protected function resolveRoute($route) {
        if (false === strpos($route, '{')) {
            return array('', array());
        }
        else {// 带参数route,通过正则表达式匹配
            // 提取参数
            preg_match_all('/\{[\w\d_]+\}/m', $route, $matches);
            $parameters = [];
            foreach($matches[0] as $matche) {
                $parameters[] = trim($matche, '{}');
            }

            // 有重复值
            // FIXME
            if (count($parameters) !== count(array_unique($parameters))) {
                throw new RouteResolveException("route中有重复值: $route");
            }

            $route = preg_replace('/\{[\w\d_]+\}/m', '%', $route);
            $routeRexPattern = '/^' . str_replace('%', '([\d\w]+)', preg_quote($route, '/')) . '$/m';

            return array($routeRexPattern, $parameters);
        }
    }

    /**
     * 分析$action, 其形式为 'moduleName.controllerName@functionName', 其中moduleName默认为'App'
     * @param  [type] $action [description]
     * @return array [type]         [description]
     * @throws RouteResolveException
     */
    protected function resolveAction($action) {
        if (strpos($action, '@') < 1) {
            throw new RouteResolveException('Route注册解析时错误.必须要合法的action名字');
        }
        $moduleName = 'App';
        if (strpos($action, '.') > 0) {
            list($moduleName, $action) = explode('.', $action);
        }

        return array_merge(array($moduleName), explode('@', $action));
    }

    /**
     * 增加route
     * @param [type] $key      [description]
     * @param [type] $routeMap [description]
     */
    protected function setRoute($key, $routeMap) {
        if ($this->collection->has($key)) {
            throw new RouteResolveException("已经存在相同的route: $key");
        }

        $this->collection->put($key, $routeMap);
        if (!isset($routeMap['map'])) {
            // 设置ROUTE反转，即从对应Action找到相应的URL，多个URL对应一个Action的情况，将相互覆盖而不提示
            if ('App' == $routeMap['action']['module']) {
                $this->reverse->put($routeMap['action']['controller'] . '@' . $routeMap['action']['method'], $key);
            }
            else {
                $this->reverse->put($routeMap['action']['module'] . '.' . $routeMap['action']['controller'] . '@' . $routeMap['action']['method'], $key);
            }
        }
    }

    /**
     * 合并两个Route集合
     * @param  RouteCollection $collection [description]
     * @return [type]                      [description]
     */
    public function merge(RouteCollection $collection) {
        foreach ($collection->all() as $key => $routeMap) {
            $this->setRoute($key, $routeMap);
        }
    }

    /**
     * route数组
     * @return ParameterBag
     */
    public function all() {
        return $this->collection->all();
    }

    public function get($routeMapKey) {
        return $this->collection->has($routeMapKey) ? $this->collection->get($routeMapKey) : null;
    }

    public function reverse($action) {
        $reverse = $this->reverse->get($action);
        if (null == $reverse) {
            return null;
        }
        return $this->collection->get($reverse);
    }

    public function reverses() {
        return $this->reverse->all();
    }

}